package com.night.icm.service.business.service;

import com.github.wenhao.jpa.PredicateBuilder;
import com.github.wenhao.jpa.Specifications;
import com.night.icm.common.exception.NoSuchDataException;
import com.night.icm.common.jpa.PageHelper;
import com.night.icm.common.night_enum.CourseBaseType;
import com.night.icm.common.night_enum.StudyStatus;
import com.night.icm.common.service.PageDataUtil;
import com.night.icm.common.utils.PageDataDTO;
import com.night.icm.common.utils.TermType;
import com.night.icm.model.business.StudentCourseDTO;
import com.night.icm.persistence.base.entity.Course;
import com.night.icm.persistence.base.entity.Student;
import com.night.icm.persistence.base.entity.Teacher;
import com.night.icm.persistence.base.repository.StudentRepository;
import com.night.icm.persistence.business.entity.CourseTeacher;
import com.night.icm.persistence.business.entity.StudentCourse;
import com.night.icm.persistence.business.entity.StudentCourseGrade;
import com.night.icm.persistence.business.reporsitory.CourseTeacherRepository;
import com.night.icm.persistence.business.reporsitory.StudentCourseGradeRepository;
import com.night.icm.persistence.business.reporsitory.StudentCourseRepository;
import com.night.icm.service.business.mapper.StudentCourseMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * StudentCourseServiceImpl
 *
 * @author Liao ZhiYong
 * @date 2020/2/15
 */
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class StudentCourseServiceImpl implements StudentCourseService {

    private final StudentCourseRepository studentCourseRepository;
    private final StudentCourseMapper studentCourseMapper;
    private final CourseTeacherRepository courseTeacherRepository;
    private final StudentRepository studentRepository;
    private final StudentCourseGradeRepository studentCourseGradeRepository;

    @Autowired
    public StudentCourseServiceImpl(StudentCourseRepository studentCourseRepository, StudentCourseMapper studentCourseMapper, CourseTeacherRepository courseTeacherRepository, StudentRepository studentRepository, StudentCourseGradeRepository studentCourseGradeRepository) {
        this.studentCourseRepository = studentCourseRepository;
        this.studentCourseMapper = studentCourseMapper;
        this.courseTeacherRepository = courseTeacherRepository;
        this.studentRepository = studentRepository;
        this.studentCourseGradeRepository = studentCourseGradeRepository;
    }

    /**
     * 分页查找选课信息
     *
     * @param page        页号，从0开始
     * @param size        每页记录条数
     * @param sort        排序字段，例如：字段1,asc,字段2,desc
     * @param studentId   所属学生
     * @param courseName  课程名称
     * @param teacherName 授课老师
     * @param year        所属年度
     * @param termType    所属学期
     * @param studyStatus 修读状态
     * @return 选课信息
     */
    @Override
    public PageDataDTO<StudentCourseDTO> findOnePage(int page, int size, String sort, String studentId, String courseName, String teacherName, String year, TermType termType, StudyStatus studyStatus) {
        Specification<StudentCourse> specification = ((Root<StudentCourse> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
            List<Predicate> conditions = new ArrayList<>();
            if (!StringUtils.isAllBlank(studentId, courseName, teacherName, year) || null != termType || null != studyStatus) {
                if (StringUtils.isNotBlank(year)) {
                    conditions.add(cb.and(cb.equal(root.get("year").as(Integer.class), Integer.valueOf(year))));
                }
                if (null != termType) {
                    conditions.add(cb.and(cb.equal(root.get("termType").as(TermType.class), termType)));
                }
                if (null != studyStatus) {
                    conditions.add(cb.and(cb.equal(root.get("studyStatus").as(String.class), studyStatus)));
                }
                Join<StudentCourse, Student> studentJoin = root.join("student", JoinType.LEFT);
                if (StringUtils.isNotBlank(studentId)) {
                    conditions.add(cb.and(cb.equal(studentJoin.get("id").as(Long.class), Long.valueOf(studentId))));
                }
                Join<StudentCourse, CourseTeacher> courseTeacherJoin = root.join("courseTeacher", JoinType.LEFT);
                if (StringUtils.isNotBlank(courseName)) {
                    Join<CourseTeacher, Course> courseJoin = courseTeacherJoin.join("course", JoinType.LEFT);
                    conditions.add(cb.and(cb.like(courseJoin.get("name").as(String.class), "%" + courseName + "%")));
                }
                if (StringUtils.isNotBlank(teacherName)) {
                    Join<CourseTeacher, Teacher> teacherJoin = courseTeacherJoin.join("teacher", JoinType.LEFT);
                    conditions.add(cb.and(cb.like(teacherJoin.get("name").as(String.class), "%" + teacherName + "%")));
                }
            }
            if (!conditions.isEmpty()) {
                query.where(conditions.toArray(new Predicate[0]));
            }
            return null;
        });
        Page<StudentCourse> page1 = this.studentCourseRepository.findAll(specification, PageHelper.generatePageRequest(page, size, sort));
        List<StudentCourseDTO> dtos = this.studentCourseMapper.toList(page1.getContent());
        return PageDataUtil.toPageData(page1, dtos);
    }

    /**
     * 添加选课信息
     *
     * @param studentCourseDTO 选课信息
     * @return 选课信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public StudentCourseDTO create(StudentCourseDTO studentCourseDTO) {
        StudentCourse studentCourse = this.studentCourseMapper.toEntity(studentCourseDTO);
        // 关联的学生
        // 关联的开课课程
        this.studentCourseRepository.save(studentCourse);
        // 添加课程后初始化成绩----> start
        StudentCourseGrade studentCourseGrade = new StudentCourseGrade();
        studentCourseGrade.setYear(studentCourse.getYear());
        studentCourseGrade.setTermType(studentCourse.getTermType());
        studentCourseGrade.setAcademyName(studentCourseDTO.getStudent().getAcademyName());
        studentCourseGrade.setCourseCode(studentCourse.getCourseTeacher().getCourse().getCode());
        studentCourseGrade.setCourseName(studentCourse.getCourseTeacher().getCourse().getName());
        studentCourseGrade.setStudentCode(studentCourse.getStudent().getCode());
        studentCourseGrade.setStudentName(studentCourse.getStudent().getName());
        studentCourseGrade.setClassName(studentCourseDTO.getStudent().getClassName());
        studentCourseGrade.setCredit(studentCourse.getCourseTeacher().getCourse().getCredit());
        // 判断课程类型是否有基础类型
        String courseTypeName = studentCourse.getCourseTeacher().getCourse().getCourseType().getName();
        CourseBaseType courseBaseType = studentCourse.getCourseTeacher().getCourse().getCourseType().getCourseBaseType();
        String courseType = courseBaseType == null ? (courseTypeName + "/" + courseTypeName) : courseTypeName;
        // 设置课程的类型
        studentCourseGrade.setCourseType(courseType);
        studentCourseGrade.setEvaluationMode(studentCourse.getCourseTeacher().getEvaluationMode());
        studentCourseGrade.setProperty("初修");
        studentCourseGrade.setGrade(studentCourseGrade.getEvaluationMode() != null && "考核".equals(studentCourseGrade.getEvaluationMode()) ? "暂无成绩" : "0");
        studentCourseGrade.setGetCredit(0.0);
        studentCourseGrade.setGradePoint(0.0);
        studentCourseGrade.setCreditGradePoint(0.0);
        studentCourseGrade.setIsCorrect(false);
        studentCourseGrade.setStudentCourseGradeGather(null);
        // 关联学生选课信息
        studentCourseGrade.setStudentCourse(studentCourse);
        this.studentCourseGradeRepository.save(studentCourseGrade);
        // 添加课程后初始化成绩----> end
        return this.studentCourseMapper.toDTO(studentCourse);
    }

    /**
     * 根据id获得开课课程
     *
     * @param id 开课课程id
     * @return 开课课程
     */
    private CourseTeacher getCourseTeacherById(String id) {
        Optional<CourseTeacher> courseTeacher = this.courseTeacherRepository.findById(Long.valueOf(id));
        if (!courseTeacher.isPresent()) {
            throw new NoSuchDataException(id);
        }
        return courseTeacher.get();
    }

    /**
     * 根据id获得学生
     *
     * @param id 学生id
     * @return 学生
     */
    private Student getStudentById(Long id) {
        Optional<Student> student = this.studentRepository.findById(id);
        if (!student.isPresent()) {
            throw new NoSuchDataException(id);
        }
        return student.get();
    }

    /**
     * 删除选课信息
     * 已结课的选课信息不能删除，前端判断
     *
     * @param id 选课id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(String id) {
        StudentCourse studentCourse = this.getOne(id);
        if (studentCourse.getStudyStatus() == StudyStatus.SELECKED_CLASS) {
            // 查找关联的学生课程成绩和对应的成绩汇总，进行同步删除
            List<StudentCourseGrade> studentCourseGrades = this.findByStudentCourseId(id);
            if (!studentCourseGrades.isEmpty()) {
                this.studentCourseGradeRepository.deleteByStudentCourseId(Long.valueOf(id));
            }
            this.studentCourseRepository.deleteById(Long.valueOf(id));
        } else {
            // 该学生课程在修未结课或已结课，不可删除
            return false;
        }
        return true;
    }

    /**
     * 根据学生选课id查找学生课程成绩
     *
     * @param studentCourseId 学生选课id
     * @return 学生课程成绩
     */
    private List<StudentCourseGrade> findByStudentCourseId(String studentCourseId) {
        PredicateBuilder<StudentCourseGrade> pb = Specifications.<StudentCourseGrade>and()
                .eq(StringUtils.isNotBlank(studentCourseId), "studentCourse.id", studentCourseId);
        return this.studentCourseGradeRepository.findAll(pb.build());
    }

    /**
     * 更新选课信息
     *
     * @param id               选课id
     * @param studentCourseDTO 选课信息
     * @return 选课信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public StudentCourseDTO update(String id, StudentCourseDTO studentCourseDTO) {
        StudentCourse studentCourse = this.getOne(id);
        this.studentCourseMapper.updateEntity(studentCourseDTO, studentCourse);
        if (!studentCourse.getCourseTeacher().getId().equals(Long.valueOf(studentCourseDTO.getCourseTeacher().getId()))) {
            CourseTeacher courseTeacher = this.getCourseTeacherById(studentCourseDTO.getCourseTeacher().getId());
            studentCourse.setCourseTeacher(courseTeacher);
        }
        this.studentCourseRepository.save(studentCourse);
        return this.studentCourseMapper.toDTO(studentCourse);
    }

    /**
     * 根据id查找选课信息
     *
     * @param id 选课id
     * @return 选课信息
     */
    @Override
    public StudentCourseDTO findById(String id) {
        StudentCourse studentCourse = this.getOne(id);
        return this.studentCourseMapper.toDTO(studentCourse);
    }

    /**
     * 根据id获取选课信息
     *
     * @param id 选课id
     * @return 选课信息
     */
    private StudentCourse getOne(String id) {
        Optional<StudentCourse> studentCourse = this.studentCourseRepository.findById(Long.valueOf(id));
        if (!studentCourse.isPresent()) {
            throw new NoSuchDataException(id);
        }
        return studentCourse.get();
    }
}